
/* Generated by Interface Builder */

#import "INewsgroupInfoD.h"
#import "IZIntPair.h"
#import "data_types.h"
#import "errdebug.h"
#import <objc/List.h>
#import <appkit/Panel.h>
#import <stdio.h>
#import <string.h>
#import <objc/zone.h>

#import "Localization.h"

#define LoStr(key)      doLocalString(NULL,key,NULL)


@implementation INewsgroupInfoD

- initWithKey:(const char *)dataKey
{
    iPairList = [[List allocFromZone:[self zone]] init];
    [super initWithKey:dataKey];
    return self;
}

- (BOOL)checkArticleIsRead:(int)kartnum
{
    int		ti;
    IZIntPair	*intPair;
    
    [self _readArticleNumSet];		/* set art_num_set to ipair_a_num[] */
					/* if it's already read : return YES */
					/*         not read:      return NO */
    for (ti=0; intPair=[iPairList objectAt:ti]; ++ti) {
	if (([intPair first] <= kartnum) && (kartnum <= [intPair last]))
	    return YES;
    }
    return NO;
}

- (void)markReadArticle:(int)kartnum;
{
    int		i;
    IZIntPair	*intPair, *lastIntPair, *rightPair, *newPair;
    int		maxIndex;

    [self _readArticleNumSet];
    if ([self checkArticleIsRead:kartnum]) /*set art_num_set to ipair_a_num */ 
	return;				   /*if already marked, return */

    /* handle last object */
    lastIntPair = [iPairList lastObject];
    if (([iPairList count]==0) || ([lastIntPair last] < kartnum)){
	if (([lastIntPair last] +1) == kartnum ) {
	    /* xxx-xxx type */
	    [lastIntPair setLast:kartnum];
	} else {
	    /* xxx-xxx,xxx type */
	    newPair = [[IZIntPair allocFromZone:[self zone]] init];
	    [newPair setFirst:kartnum last:kartnum];
	    [iPairList addObject:newPair];
	}
        [self _writeArticleNumSet];
        return;
    }

    maxIndex = [iPairList count] - 1;
    for (i=(maxIndex-1); i>=0; i--) {
	intPair = [iPairList objectAt:i];
	rightPair = [iPairList objectAt:(i+1)];
	if (([intPair last] < kartnum) && (kartnum < [rightPair first])) {
	    if (([intPair last]+1 == kartnum) 
	    	   && ( kartnum+1 == [rightPair first])) {
		/* 0-219,210,211-220 -> 0-220 type */
		[intPair setLast:[rightPair last]];
		/* remove rightPair */
		[iPairList removeObjectAt:(i+1)];
		[rightPair free];
	    } else if (([intPair last]+1) == kartnum) {
		/* 0-120,121,210-220 -> 0-121,210-220 type */
		[intPair setLast:kartnum];
	    } else if ((kartnum+1) == [rightPair first]) {
		/* 0-120,219,210-220 -> 0-120,219-220 type */
		[rightPair setFirst:kartnum];
	    } else {
		/* 0-120,150,210-220 type */
		/* insert object */
		newPair = [[IZIntPair allocFromZone:[self zone]] init];
		[iPairList insertObject:newPair at:(i+1)];
		[newPair setFirst:kartnum last:kartnum];
	    }
	}
    }
    /* put read-article data to groupInfo */
    [self _writeArticleNumSet];
    
    return;
}

- (BOOL)isAllArticleMarked
{
    int		first, last;
    int		i;
    
    first = (int)[self infoForKey:FIRST];
    last  = (int)[self infoForKey:LAST];
    
    for (i=first; i<=last; ++i) {
	if ([self checkArticleIsRead:i]==NO) {
	    return NO;
	}
    }
    return YES;
}

- (int)countArticleUnMarked
{
    int		first, last;
    int		i;
    int		art_count;
    
    first = (int)[self infoForKey:FIRST];
    last  = (int)[self infoForKey:LAST];
    
    art_count = 0;
    for (i=first; i<=last; ++i) {
	if ([self checkArticleIsRead:i]==NO) {
	    ++art_count;
	}
    }
    return art_count;
}

- (void)unmarkReadArticle:(int)kartnum
{
    int		i;
    IZIntPair	*intPair, *newPair;

    [self _readArticleNumSet];		 /*set art_num_set to ipair_a_num */ 
    if (!([self checkArticleIsRead:kartnum]))
	return;				   /*if not marked, return */
	
    for (i=0; intPair=[iPairList objectAt:i]; i++) {
	/* xxx */
	if (kartnum==[intPair first] && kartnum==[intPair last]) {
	    /* remove xxx */
	    if ([intPair first] == kartnum) {
		/* copy, 123-150,176,180-210 -> 123-150,180-210 */
		[iPairList removeObjectAt:i];
		[intPair free];
		break;
	    }
	/* xxx-xxx */
	} else if ([intPair first] == kartnum) {
	    /* 123-150 -> 124-150 */
	    [intPair setFirst:([intPair first]+1)];
	    break;
	} else if ([intPair last] == kartnum) {
	    /* 123-150 -> 123-149 */
	    [intPair setLast:([intPair last]-1)];
	    break;
	} else if (([intPair first] < kartnum) && (kartnum < [intPair last])) {
	    /* 123-150 -> 123-135,137-150 */
	    /* copy */
	    newPair = [[IZIntPair allocFromZone:[self zone]] init];
	    [iPairList insertObject:newPair at:(i+1)];
	    [newPair setFirst:(kartnum+1) last:[intPair last]];
	    [intPair setLast:(kartnum-1)];
	    break;
	}
    }
    /* put read-article data to groupInfo */
    [self _writeArticleNumSet];
    return;
}

- (void)markAllArticle
{
    IZIntPair	*intPair;
    
    [iPairList freeObjects];
    intPair = [[IZIntPair allocFromZone:[self zone]] init];
    [iPairList addObject:intPair];
    [intPair setFirst:0 last:(int)[self infoForKey:LAST]];
    [self _writeArticleNumSet];
    return;
}

- (void)_readArticleNumSet
{
    char	*art_num_set, ch;
    int		first, last;
    int		num_read;
    IZIntPair	*intPair;
    
    if ([iPairList count] > 0) {
        return;
    }
    art_num_set = (char *)[self infoForKey:ART_NUM_SET];
    
				/* store data to instance val. */
    ch=' ';
    while ((num_read = sscanf(art_num_set,"%d-%d", &first, &last))
                && ch !='\0') {
	intPair = [[IZIntPair allocFromZone:[self zone]] init];
	[iPairList addObject:intPair];

	if (num_read == 2) {			/* xxx-xxx type */
	    [intPair setFirst:first last:last];	    
	} else {				/* xxx type */
	    [intPair setFirst:first last:first];
	} 					/* descripter is '.' or ' ' */
						/* and go to next descripter */
	/* skip the pointer(art_num_set) to the head of next set */
//	while(!((ch = *(art_num_set++)) == ','  || ch == ' ' || ch =='\0'))
	while(!((ch = *(art_num_set++)) == ','  || ch =='\0'))
            ;
    }
    if ([iPairList count] == 0) {
	intPair = [[IZIntPair allocFromZone:[self zone]] init];
	[iPairList addObject:intPair];
	[intPair setFirst:(int)0 last:(int)0];
    }
    return;
}

- (void)_writeArticleNumSet
{
#define		LINE_MAX_LEN		4088

    int		i;
    char	line_buf[LINE_MAX_LEN], *line_buf_ptr;
    IZIntPair	*intPair;
						/* print art num. set */    
    line_buf[0] = '\0';
    line_buf_ptr = line_buf;

    for (i=0; intPair=[iPairList objectAt:i]; ++i) {
	if (strlen(line_buf) > (LINE_MAX_LEN - 24)) {
	    /* 4 byte integer needs 10 chars, so 20. and '-'','' ''\0' */
	    NXRunAlertPanel(LoStr("NewsBase"),
		LoStr("newsrc file: length of line over flow"),
		LoStr("OK"),NULL,NULL);
	    return;
	}
	/* move pointer to string's end */
        line_buf_ptr = strchr(line_buf, '\0');
	if ([intPair first] < [intPair last]) {
							/* ..., xxx-xxx */
	    sprintf(line_buf_ptr, "%d-%d",
	    		(int)[intPair first], (int)[intPair last]);
	} else {
							/* ..., xxx */
	    sprintf(line_buf_ptr, "%d", (int)[intPair first]);
	}
					/* move pointer to string's end */
	line_buf_ptr = strchr(line_buf, '\0');
	if (i != ([iPairList count]-1)) {
	    						/* add ',' */
	    sprintf (line_buf_ptr, ",");
	}
    }
    DBG(1,fprintf (stderr," art_num_set = %s\n", line_buf));
    [self addInfoString:line_buf key:ART_NUM_SET];
}

- (int)firstArticleForAmount:(int)artWillGetNum
{
    int		amount;
    int		last;
    int		i;
    
    if (artWillGetNum==0) {
	return 0;
    }
    last = (int)[self infoForKey:LAST];
    amount = [self countArticleUnMarked];
    i = last + 1;
    
    while ((artWillGetNum > 0) && (amount > 0) && (--i > 0)) {
	if ([self checkArticleIsRead:i]==NO) {
	    /* article is unmarked one */
	    --artWillGetNum;
	    --amount;
	}
    }
    return i;
}

- addInfoInt:(int)theValue key:(char *)theKey;
{
    unsigned int	i, ti;
    int		first, last, ex_last;
    IZIntPair	*intPair;

    if (strcmp(theKey,FIRST) == 0) {
	/* key is FIRST, try to clean up ART_NUM_SET */
	if ([self infoForKey:ART_NUM_SET] != nil) {
	    /* this groupinfo has ART_NUM_SET, iPairList is set */
	    for (i=[iPairList count]-1;
		    ((intPair=[iPairList objectAt:i])!=nil && i!=0); i--) {
		first = [intPair first];
		last = [intPair last];
		ex_last = theValue - 1;
		/* if there is "1, ... xxx-yyy .... theValue", */
		/* remove article # set under theValue(i.e. xxx-yyy) */
		/* i.e. remove "1-ex_last" */
		
		if (ex_last < (first-1)) {
		    /* theValue ... xxx-yyy */
		    continue;
		}

		if ((first-1) <= ex_last && ex_last <= last) {
		    /* theValue is 124, so 0-123 is already read */
		    /*      0, ..., 123-130 -> 0-130, ... */
		    /* or,  0, ...., 120-130 -> 0-130 */
		    /* or,  0, ...., 120-123 -> 0-123 */
		    for (ti=i; ti>=1; --ti) {
			[iPairList removeObjectAt:ti];
		    }
		    [[iPairList objectAt:0] setFirst:0 last:last];
		    break;
		} else if (last < ex_last) {
		    /*      0, ...., xxx-120, 130-135 -> 0-123, 130-135,...*/
		    for (ti=i; ti>=1; --ti) {
			[iPairList removeObjectAt:ti];
		    }
		    [[iPairList objectAt:0] setFirst:0 last:ex_last];
		    break;
		} 
	    }
	} 
    }

    return [super addInfoInt:theValue key:theKey];
}

@end
